// Copyright (C) Kumo inc. and its affiliates.
// Author: Jeff.li lijippy@163.com
// All rights reserved.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <https://www.gnu.org/licenses/>.
//

#pragma once

#include <cantor/unresolved/visitor.h>
#include <turbo/log/logging.h>

namespace cantor::unresolved {
    class UnresolvedConstExpr;
    class UnresolvedEnumElement;
    class UnresolvedNamedType;
    class UnresolvedTypeNameUri;

    class UnresolvedTypeStmt : public UnresolvedVisitor<UnresolvedTypeStmt> {
    public:
        static constexpr const cantor::pb::ast::StmtType TYPE = cantor::pb::ast::StmtType::STMT_TYPE_NAME;

        UnresolvedTypeStmt(const cantor::pb::ast::TypeStmt &node) : instance(node) {
        }

        UnresolvedTypeStmt(const cantor::pb::ast::Statement &node) : instance(node.type_stmt()) {
        }

        ~UnresolvedTypeStmt() override = default;

        const pb::Location &location_impl() const {
            return instance.location();
        }

        const pb::Status &error_status_impl() const {
            return instance.status();
        }

        size_t names() const {
            return instance.names_size();
        }

        std::string_view name(const size_t idx) const {
            KCHECK(idx < names());
            return instance.names(idx);
        }

        bool has_type_oid() const {
            return instance.has_type_oid();
        }

        uint32_t type_oid() const {
            KCHECK(has_type_oid());
            return instance.type_oid();
        }

        bool has_setof() const {
            return instance.has_setof();
        }

        bool setof() const {
            KCHECK(has_setof());
            return instance.setof();
        }

        bool has_pct_type() const {
            return instance.has_pct_type();
        }

        bool pct_type() const {
            KCHECK(has_pct_type());
            return instance.pct_type();
        }

        size_t type_mods() const {
            return instance.type_mods().size();
        }

        UnresolvedConstExpr type_mod(const size_t idx) const;

        size_t dimension_size() const {
            return instance.array_bounds_size();
        }

        int64_t dimension(const size_t idx) const;

        std::vector<int64_t> dimensions() const;

        cantor::pb::ast::TypeNameType type() const {
            return instance.type();
        }

        std::string type_str() const {
            return cantor::pb::ast::TypeNameType_Name(instance.type());
        }

        /// TYPE_NAME_GENERIC

        bool is_generic() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_GENERIC;
        }

        /// TYPE_NAME_NUMERIC
        bool is_numeric() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_NUMERIC;
        }

        cantor::pb::ast::NumericType numeric_type() const {
            KCHECK(is_numeric());
            return instance.numeric_arg().numeric_type();
        }

        std::string numeric_type_str() const {
            KCHECK(is_numeric());
            return cantor::pb::ast::NumericType_Name(instance.numeric_arg().numeric_type());
        }

        /// TYPE_NAME_DATETIME
        bool is_datetime() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_DATETIME;
        }

        cantor::pb::ast::TimeStampPrecision datetime_precision() const {
            KCHECK(is_datetime());
            return instance.datatime_arg().precision();
        }

        std::string datetime_precision_str() const {
            return cantor::pb::ast::TimeStampPrecision_Name(datetime_precision());
        }

        /// TYPE_NAME_TIMESTAMP
        bool is_timestamp() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_TIMESTAMP;
        }

        cantor::pb::ast::TimeStampPrecision timestamp_precision() const {
            KCHECK(is_timestamp());
            return instance.timestamp_arg().precision();
        }

        std::string timestamp_precision_str() const {
            return cantor::pb::ast::TimeStampPrecision_Name(timestamp_precision());
        }

        /// TYPE_NAME_INTERVAL
        bool is_interval() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_INTERVAL;
        }

        cantor::pb::ast::TimeStampPrecision interval_precision() const {
            KCHECK(is_interval());
            return instance.interval_arg().precision();
        }

        std::string interval_precision_str() const {
            return cantor::pb::ast::TimeStampPrecision_Name(interval_precision());
        }

        /// TYPE_NAME_DURATION
        bool is_duration() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_DURATION;
        }

        cantor::pb::ast::TimeStampPrecision duration_precision() const {
            KCHECK(is_duration());
            return instance.duration_arg().precision();
        }

        std::string duration_precision_str() const {
            return cantor::pb::ast::TimeStampPrecision_Name(duration_precision());
        }

        /// TYPE_NAME_JSON
        bool is_json() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_JSON;
        }

        /// TYPE_NAME_BITMAP
        bool is_bitmap() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_BITMAP;
        }

        /// TYPE_NAME_TDIGEST
        bool is_tdigest() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_TDIGEST;
        }

        /// TYPE_NAME_HLL
        bool is_hll() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_HLL;
        }

        /// TYPE_NAME_STRING
        bool is_string() const {
            if (type() != cantor::pb::ast::TypeNameType::TYPE_NAME_STRING) {
                return false;
            }
            if (!instance.has_string_arg()) {
                return false;
            }
            if (instance.string_arg().is_binary()) {
                return false;
            }
            return true;
        }

        bool is_binary() const {
            if (type() != cantor::pb::ast::TypeNameType::TYPE_NAME_STRING) {
                return false;
            }
            if (!instance.has_string_arg()) {
                return false;
            }
            return instance.string_arg().is_binary();
        }

        std::string_view charset() const {
            KCHECK(type() != cantor::pb::ast::TypeNameType::TYPE_NAME_STRING && instance.has_string_arg());
            if (!instance.string_arg().has_charset()) {
                return std::string_view();
            }
            return instance.string_arg().charset();
        }

        std::string_view collate() const {
            KCHECK(type() != cantor::pb::ast::TypeNameType::TYPE_NAME_STRING&& instance.has_string_arg());
            if (!instance.string_arg().has_collate()) {
                return std::string_view();
            }
            return instance.string_arg().collate();
        }

        /// TYPE_NAME_MAP
        bool is_map() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_MAP;
        }

        UnresolvedTypeStmt map_key() const {
            KCHECK(is_map());
            return UnresolvedTypeStmt(instance.map_arg().key());
        }

        UnresolvedTypeStmt map_value() const {
            KCHECK(is_map());
            return UnresolvedTypeStmt(instance.map_arg().value());
        }

        /// TYPE_NAME_SET

        bool is_set() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_SET;
        }

        UnresolvedTypeStmt set_key() const {
            KCHECK(is_set());
            return UnresolvedTypeStmt(instance.set_arg().key());
        }

        /// TYPE_NAME_TUPLE

        bool is_tuple() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_TUPLE;
        }

        size_t tuple_size() const {
            KCHECK(is_tuple());
            return instance.tuple_arg().types_size();
        }

        std::vector<UnresolvedTypeStmt> tuple_types() const {
            KCHECK(is_tuple());
            std::vector<UnresolvedTypeStmt> result;
            for (size_t i = 0; i < instance.tuple_arg().types_size(); ++i) {
                result.emplace_back(UnresolvedTypeStmt(instance.tuple_arg().types(i)));
            }
            return result;
        }

        UnresolvedTypeStmt tuple_type(size_t index) const {
            KCHECK(is_tuple());
            KCHECK(index < instance.tuple_arg().types_size());
            return UnresolvedTypeStmt(instance.tuple_arg().types(index));
        }

        /// TYPE_NAME_UNION
        bool is_union() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_UNION;
        }

        std::vector<UnresolvedTypeStmt> union_types() const {
            KCHECK(is_union());
            std::vector<UnresolvedTypeStmt> result;
            for (size_t i = 0; i < instance.union_arg().types_size(); ++i) {
                result.emplace_back(UnresolvedTypeStmt(instance.union_arg().types(i).type()));
            }
            return result;
        }

        std::vector<std::string_view> union_names() const {
            KCHECK(is_union());
            std::vector<std::string_view> result;
            for (size_t i = 0; i < instance.union_arg().types_size(); ++i) {
                result.emplace_back(instance.union_arg().types(i).name());
            }
            return result;
        }

        UnresolvedTypeStmt union_type(size_t index) const {
            KCHECK(is_union());
            KCHECK(index < instance.union_arg().types_size());
            return UnresolvedTypeStmt(instance.union_arg().types(index).type());
        }

        std::string_view union_name(size_t index) const {
            KCHECK(is_union());
            KCHECK(index < instance.union_arg().types_size());
            return instance.union_arg().types(index).name();
        }

        std::vector<UnresolvedNamedType> union_fields() const;

        UnresolvedNamedType union_field(size_t index) const;

        /// TYPE_NAME_STRUCT

        bool is_struct() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_STRUCT;
        }

        std::vector<UnresolvedTypeStmt> struct_types() const {
            KCHECK(is_struct());
            std::vector<UnresolvedTypeStmt> result;
            for (size_t i = 0; i < instance.struct_arg().types_size(); ++i) {
                result.emplace_back(UnresolvedTypeStmt(instance.struct_arg().types(i).type()));
            }
            return result;
        }

        std::vector<std::string_view> struct_names() const {
            KCHECK(is_struct());
            std::vector<std::string_view> result;
            for (size_t i = 0; i < instance.struct_arg().types_size(); ++i) {
                result.emplace_back(instance.struct_arg().types(i).name());
            }
            return result;
        }

        UnresolvedTypeStmt struct_type(size_t index) const {
            KCHECK(is_struct());
            KCHECK(index < instance.struct_arg().types_size());
            return UnresolvedTypeStmt(instance.struct_arg().types(index).type());
        }

        std::string_view struct_name(size_t index) const {
            KCHECK(is_struct());
            KCHECK(index < instance.struct_arg().types_size());
            return instance.struct_arg().types(index).name();
        }

        std::vector<UnresolvedNamedType> struct_fields() const;

        UnresolvedNamedType struct_field(size_t index) const;

        /// TYPE_NAME_PROTO
        bool is_proto() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_PROTO;
        }

        std::string_view proto_uri() const;

        /// TYPE_NAME_ENUM
        bool is_enum() const {
            return type() == cantor::pb::ast::TypeNameType::TYPE_NAME_ENUM;
        }

        size_t num_elements() const {
            KCHECK(is_union());
            return instance.enum_arg().element_size();
        }

        UnresolvedEnumElement enum_element(size_t index) const;

        std::vector<UnresolvedEnumElement> enum_elements() const;

        UnresolvedTypeNameUri enum_uri() const;

        const cantor::pb::ast::TypeStmt &instance;
    };
} // namespace cantor::unresolved
